
#include <asset/assetsystem.h>
#include <asset/reader/assetreadermaterial.h>
#include <fs/fileinfo.h>
#include <gfx/ieffect.h>
#include <gfx/itexture.h>
#include <gfx/material.h>
#include <vfs.h>
#include <cffformat.h>

ceAssetReaderMaterial::ceAssetReaderMaterial()
{
}



bool ceAssetReaderMaterial::CanRead(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
	if (type != ceMaterial::AssetTypeID)
		{
			return false;
		}

	ceFileInfo info (locator.GetLocation());
	std::string ext = info.GetExtension();
	return ext == "mat";
}


cffNode* ceAssetReaderMaterial::FindNode(cffNode *parent, const char *name)
{
	for (unsigned i=0, j=parent->GetNumberOfNodes(); i<j; ++i)
		{
			cffNode* materialNode = parent->GetNode(i);
			if (materialNode
					&& materialNode->GetName() == std::string("material")
					&& materialNode->HasAttribute(0)
					&& std::string (name) == (const char*)materialNode->GetAttribute(0))
				{
					return materialNode;
				}

		}

	return 0;
}



ceRef<ceAsset> ceAssetReaderMaterial::Read(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
	ceReleasePtr<iFile> file (ceVFS::Get()->Open(locator.GetLocation()));
	if (!file)
		{
			return 0;
		}

	long size = file->GetLength();

	char* buffer = new char[size+1];
	file->Read(buffer, size);
	file->Close ();

	buffer[size] = '\0';

	cffParser parser;
	ceReleasePtr<cffNode> parent (parser.ParseFromBuffer(buffer));
  delete [] buffer;
	if (!parent)
		{
			return 0;
		}

	cffNode* materialNode = FindNode(parent, locator.GetName());
	if (!materialNode)
		{
			return 0;
		}

	ceMaterial* material = new ceMaterial ();
  material->SetLocator(locator);

	ceMaterialSpec& spec = material->GetMaterialSpec();

	spec.Ambient  = ReadColor(materialNode->GetNode("ambient"),  spec.Ambient);
	spec.Diffuse  = ReadColor(materialNode->GetNode("diffuse"),  spec.Diffuse);
	spec.Specular = ReadColor(materialNode->GetNode("specular"), spec.Specular);
	spec.Emission = ReadColor(materialNode->GetNode("emission"), spec.Emission);
	cffNode* shininessNode = materialNode->GetNode("shininess");
	if (shininessNode && shininessNode->HasAttribute(0))
		{
			spec.Shininess = shininessNode->GetAttribute(0).AsFloat();
		}

	cffNode* effectNode = materialNode->GetNode("effect");
	if (effectNode && effectNode->HasAttribute(0))
		{
			std::string effectName = (const char*)effectNode->GetAttribute(0);
			iEffect *effect = engine->GetAssetSystem()->GetAsset<iEffect>(ceAssetLocator(effectName.c_str()));
			if (!effect)
				{
					material->Release();
					return 0;
				}
			material->SetEffect(effect);
		}

	cffNode* texturesNode = materialNode->GetNode("textures");
	if (texturesNode)
		{
			for (unsigned i=0, ni=texturesNode->GetNumberOfNodes("pass"); i<ni; ++i)
				{
					cffNode *passNode = texturesNode->GetNode("pass", i);
					if (passNode && passNode->HasAttribute(0))
						{
							ceRenderPass rp = RP_Diffuse;
							std::string passName = (const char*)passNode->GetAttribute(0);
							if (passName == "depth")
								{
									rp = RP_Depth;
								}
							else if (passName == "diffuse")
								{
									rp = RP_Diffuse;
								}
							else if (passName == "shadow")
								{
									rp = RP_Shadow;
								}
							else if (passName == "stencil")
								{
									rp = RP_StencilVolume;
								}
							else
								{
									material->Release();
									return 0;
								}

							for (unsigned j=0, nj=passNode->GetNumberOfNodes("texture"); j<nj; ++j)
								{
									cffNode *textureNode = passNode->GetNode("texture", j);
									if (textureNode && textureNode->HasAttribute(1))
										{
											unsigned unit = textureNode->GetAttribute(0).AsInt();
											std::string name = (const char*)textureNode->GetAttribute(1);
											iTexture *texture = engine->GetAssetSystem()->GetAsset<iTexture>(ceAssetLocator(name.c_str()));
											if (texture)
												{
													material->SetTexture(rp, (ceTextureStage)(TS_Texture0 + unit), texture);
												}
										}
								}
						}
				}
		}


	return material;
}



ceColor4f ceAssetReaderMaterial::ReadColor(cffNode *colorNode, const ceColor4f &initial)
{
	ceColor4f color = initial;
	if (colorNode)
		{
			if (colorNode->HasAttribute(0))
				{
					color.r = colorNode->GetAttribute(0).AsFloat();
				}
			if (colorNode->HasAttribute(1))
				{
					color.g = colorNode->GetAttribute(1).AsFloat();
				}
			if (colorNode->HasAttribute(2))
				{
					color.b = colorNode->GetAttribute(2).AsFloat();
				}
			if (colorNode->HasAttribute(3))
				{
					color.a = colorNode->GetAttribute(3).AsFloat();
				}
		}
	return color;
}
